package com.elitel.common.businessJdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import com.elitel.common.businessJdbc.mysql.MysqlPreparedStatementStrategy;
import com.elitel.common.businessJdbc.oracle.OraclePreparedStatementStrategy;
import com.elitel.frame.main.entity.constant.DictionaryConstant;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

// Here is provider common reuse code for various preparedStatement
@Slf4j
public abstract class BusinessPreparedStatementHandler {

    // can use ThreadLocal method to handle
    @Autowired
    @Qualifier("slaveDataSource")
    DataSource slaveDataSource;

    private final static Map<String, BusinessPreparedStatementStrategy> businessPreparedStatementStrategyMap;

    static{
        businessPreparedStatementStrategyMap  = new HashMap<>();
        // add mysql strategy
        // default strategy （mysql strategy）
        businessPreparedStatementStrategyMap.put(DictionaryConstant.DatabaseType_MYSQL, new MysqlPreparedStatementStrategy());
        // add oracle strategy
        businessPreparedStatementStrategyMap.put(DictionaryConstant.DatabaseType_ORACLE, new OraclePreparedStatementStrategy());
    }


    // here support insert and update
    protected Connection getConnection() throws SQLException{
        return getBusinessDataSource().getConnection();
    }

    protected DataSource getBusinessDataSource(){
        return slaveDataSource;
    }

    public BusinessPreparedStatementStrategy getPreparedStatementMethodStrategy(String databaseVersion){
        BusinessPreparedStatementStrategy strategy = businessPreparedStatementStrategyMap.get(databaseVersion);
        if(strategy == null)
            strategy = businessPreparedStatementStrategyMap.get(DictionaryConstant.DatabaseType_MYSQL);
        return strategy;
    }


    // https://stackoverflow.com/questions/35531621/can-preparedstatements-setobject-method-be-used-for-any-datatype
    // type set method
    public boolean matchPreparedStatementSetMethodByFieldType(String databaseVersion, PreparedStatement ps, int parameterIndex, Object columnValue, String fieldType) throws Exception{
        return getPreparedStatementMethodStrategy(databaseVersion).matchPreparedStatementSetMethodByFieldType(ps, parameterIndex, columnValue, fieldType);
    }

    // here is create log for workflow sql
    @Data
    static class SqlRecorder{
        private String serviceGuid;
        private String precompileSql;
        private StringBuilder parameterPlaceHolderInformation = new StringBuilder();
        private StringBuilder errorMessages = new StringBuilder();

        // not error occur
        public boolean canExecuteSql(){
            return errorMessages.length() == 0;
        }

        public void addParameterInformation(String parameterInfo){
            parameterPlaceHolderInformation.append("\r\n\t").append(parameterInfo);
        }

        public void addErrorMessage(String message){
            errorMessages.append("\r\n\t").append(message);
        }

        public String formatInfo(){
            StringBuilder sb = new StringBuilder();
            sb.append("\r\n===========> \r\n serviceGuid (").append(serviceGuid).append(")")
                .append("\r\n precompileSql: ").append(precompileSql)
                .append("\r\n parameterInfo: ").append(parameterPlaceHolderInformation.toString())
                .append("\r\n errorMessages: ").append(errorMessages.toString())
                .append("\r\n<===========") ;
            return sb.toString();
        }

    }

}
